/*
 *
 *
Copyright (c) 1998-2001 Aristar, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. The end-user documentation included with the redistribution, if any,
must include the following acknowledgment:

"This product includes software developed by Aristar, Inc. (http://www.aristar.com/)."

Alternately, this acknowledgment may appear in the software itself, if and
wherever such third-party acknowledgments normally appear.

4. The names "Dialect" and "Aristar, Inc." must not be used to endorse or
promote products derived from this software without prior written permission.
For written permission, please contact info@aristar.com.

5. Products derived from this software may not be called "Dialect", nor may
"Dialect" appear in their name, without prior written permission of Aristar, Inc.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
ARISTAR, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software consists of voluntary contributions made by many individuals
on behalf of Aristar, Inc.  For more information on Aristar, Inc.,
please see <http://www.aristar.com/>.

Portions of this software are based upon software freely available and
written by individuals not associated with Aristar, Inc.  Copyrights are
maintained in the relevant source code files.
 *
 *
 */

// QGrid.cpp : implementation file
//

#include "stdafx.h"
#include "QGrid.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// QGrid

extern bool FrameToFont(VALUE vFrame, CFont& font); // GUIModule.cpp

CQGrid::CQGrid(
		CQWindow* pQWindow,
		RECT& rc,
		int nrows, int ncols,
		int rowHeight,
		int colWidth[],
		int labelRowHeight,
		int labelColWidth,
		VALUE vEditFont, VALUE vLabelFont,
		VALUE (*pHandler) (CQGrid*, int, VALUE),
		VALUE vSelf)
		:CGrid(), CQBase(vSelf)
{
	m_pHandler = pHandler;

	CDC dc;
	dc.CreateCompatibleDC(NULL);

	CFont *pEditFont, *pLabelFont, *old;
	if ((vEditFont.Type() == stFrame) && FrameToFont(vEditFont, m_EditFont)) {
		pEditFont = &m_EditFont;
		old = dc.SelectObject(pEditFont);
		rowHeight = dc.GetTextExtent(_T("QTyqgWi")).cy;
		dc.SelectObject(old);
	}
	else pEditFont = &g_QEditFont;

	int labelHeight = 0;
	if ((vLabelFont.Type() == stFrame) && FrameToFont(vLabelFont, m_LabelFont)) {
		pLabelFont = &m_LabelFont;
		old = dc.SelectObject(pLabelFont);
		labelHeight = dc.GetTextExtent(_T("QTyqgWi")).cy;
		dc.SelectObject(old);
	}
	else pLabelFont = &g_QLabelFont;

	if (labelHeight > rowHeight)
		rowHeight = labelHeight;
	if ((labelRowHeight != 0) && (labelHeight > 0))
		labelRowHeight = labelHeight; // labelRowHeight == 0 means no col headers!

	Create(
		pQWindow,				//CWnd* pParent,
		rc,						//const RECT & rect
		nrows,ncols,			//int nrows, int ncols
	  	rowHeight,				//int rowHeight
		colWidth,				//int colWidth[]
		labelRowHeight,			//int labelRowHeight
		labelColWidth,			//int labelColWidth
		pEditFont, //&g_QEditFont,			//CFont* cellFont
		pLabelFont //&g_QLabelFont			//CFont* labelFont
		);

	data = new CString*[m_nRows + 1];
	for (int row = 0; row <= m_nRows; ++row) {
		data[row] = new CString[m_nCols + 1];
	}
}

CQGrid::~CQGrid()
{
	for (int row = 0; row <= m_nRows; ++row) {
		delete[] (data[row]);
	}
	delete[] data;
}

BEGIN_MESSAGE_MAP(CQGrid, CGrid)
	//{{AFX_MSG_MAP(CQGrid)
	ON_WM_DESTROY()
	ON_WM_LBUTTONDBLCLK()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CQGrid::Clear()
{
	for (int row = 1; row <= m_nRows; row++) {
		for (int col = 1; col <= m_nCols; col++) {
			data[row][col].Empty();
		}
	}
	Invalidate();
}

bool CQGrid::SetContents(VALUE vArray)
{
	if (vArray.Type() == stNil) {
		Clear();
		return true;
	}
	else if ((vArray.Type() != stArray) || (vArray.Length() != m_nRows)) {
		return false;
	}

	for (int row = 1; row <= m_nRows; ++row) {
		VALUE vCols = vArray[row];
		if ((vCols.Type() != stArray) || (vCols.Length() != m_nCols)) {
			if (row > 1) Invalidate();	// We changed at least on row
			return false;
		}
		for (int col = 1; col <= m_nCols; ++col) {
			data[row][col] = vCols[col].String();
		}
	}
	Invalidate();
	return true;
}

VALUE CQGrid::GetContents()
{
	VALUE vArray = AllocArray(m_nRows);
	for (int row = 1; row <= m_nRows; ++row) {
		VALUE vCols = AllocArray(m_nCols);
		for (int col = 1; col <= m_nCols; ++col) {
			vCols.Set(col, data[row][col]);
		}
		vArray.Set(row, vCols);
	}
	return vArray;
}

bool CQGrid::SetRowHeaders(VALUE vArray)
{
	if (vArray.Type() == stNil) {
		for (int row = 1; row <= m_nRows; ++row) {
			data[row][0].Empty();
		}
	}
	else if ((vArray.Type() != stArray) || (vArray.Length() != m_nRows)) {
		return false;
	}
	else {
		for (int row = 1; row <= m_nRows; ++row) {
			data[row][0] = vArray[row].String();
		}
	}
	Invalidate();
	return true;
}

VALUE CQGrid::GetRowHeaders()
{
	VALUE vArray = AllocArray(m_nRows);
	for (int row = 1; row <= m_nRows; ++row) {
		vArray.Set(row, data[row][0]);
	}
	return vArray;
}

bool CQGrid::SetColHeaders(VALUE vArray)
{
	if (vArray.Type() == stNil) {
		for (int col = 1; col <= m_nCols; ++col) {
			data[0][col].Empty();
		}
	}
	else if ((vArray.Type() != stArray) || (vArray.Length() != m_nCols)) {
		return false;
	}
	else {
		for (int col = 1; col <= m_nCols; ++col) {
			data[0][col] = vArray[col].String();
		}
	}
	Invalidate();
	return true;
}

VALUE CQGrid::GetColHeaders()
{
	VALUE vArray = AllocArray(m_nCols);
	for (int col = 1; col <= m_nCols; ++col) {
		vArray.Set(col, data[0][col]);
	}
	return vArray;
}

void CQGrid::SetText(int row, int col, CString text)
{
	if ((row < 0) || (col < 0) || (row > m_nRows) || (col > m_nCols)) return;
	data[row][col] = text;
	Invalidate();
}

CString CQGrid::GetText(int row, int col)
{
	if ((row < 0) || (col < 0) || (row > m_nRows) || (col > m_nCols)) return _T("");
	return data[row][col];
}

void CQGrid::Set(int row, int col, CString text)
{
	SetText(++row, ++col, text);
}

CString CQGrid::Get(int row, int col)
{
	return GetText(++row, ++col);
}

int CQGrid::GetRowCount()
{
	return m_nRows;
}

int CQGrid::GetColumnCount()
{
	return m_nCols;
}

void CQGrid::SetRowCount(int rows)
{
	if (rows == m_nRows) return;
	else if (rows < m_nRows) {
		// free memory if reducing number of rows
		for (int row = rows + 1; row <= m_nRows; row++) {
			delete[] (data[row]);
		}
	}
	else {
		// Alloc more memory because we need more rows
		// Don't forget to copy the current column data
		int row;
		CString **newData = new CString*[rows + 1];
		for (row = 0; row <= m_nRows; ++row) {
			newData[row] = data[row];
		}
		for (row = m_nRows + 1; row <= rows; ++row) {
			newData[row] = new CString[m_nCols + 1];
		}
		delete[] data;	// Out with the old
		data = newData;	// In with the new
	}

	// Change the way the grid paints (also updates m_nRows/m_nCols)
	SetNumberRows(rows);
}

void CQGrid::OnFocus(int row, int col)
{
	// The original CGrid would auto-start the edit box on this
	// Let's not do that
}

void CQGrid::OnClick(int row, int col, int button, int down)
{
	if ((row == INVALID_INDEX) || (col == INVALID_INDEX)) {
		return;
	}
	else if (down) {
		SetSelection(row, col);
	}
	else if (m_pHandler) {
		m_pHandler(this, QW_CLICK, VALUE());
	}
}

void CQGrid::OnLButtonDblClk(UINT nFlags, CPoint point)
{
	int row, col;
	GetCell(point,&row,&col);
	if ((row != INVALID_INDEX) && (col != INVALID_INDEX)) {
		SetSelection(row, col);
		if (m_pHandler) {
			m_pHandler(this, QW_DOUBLECLICK, VALUE());
		}
	}
}

void CQGrid::OnDestroy()
{
	CGrid::OnDestroy();
	if (m_pHandler) m_pHandler(this, QW_DESTROY, VALUE());	// Causes garbage collection
}

void CQGrid::OnSelChange()
{
	if (m_pHandler) {
		m_pHandler(this, QW_SELCHANGE, VALUE());
	}
}
